﻿using System;
using System.Collections.Generic;
using System.IO;
using Xenta.Attributes;
using Xenta.Enums;
using Xenta.Operations;
using Xenta.Plugins;
using Xenta.Utils;

namespace Xenta.Api
{
    /// <summary>
    /// Represents the export service.
    /// </summary>
    [Dependency("63fd2334-2682-4c83-b939-1b3dc2a18b76",
        Lifestyle = DependencyLifestyle.Singleton)]
    public class ExportService : IHandler
    {
        #region Fields

        private readonly IInfrastructure _infrastructure;

        #endregion

        #region Ctors

        /// <summary>
        /// Initializes a new instance of the class.
        /// </summary>
        /// <param name="infrastructure">The infrastructure.</param>
        public ExportService(IInfrastructure infrastructure)
        {
            _infrastructure = infrastructure;
        }

        #endregion

        #region Methods

        /// <summary>
        /// Handles the input stream, using the ingoing properties, 
        /// and returns the result to the output stream, filling 
        /// the outgoing properties.
        /// </summary>
        /// <param name="mimeType">The MIME type string reference.</param>
        /// <param name="inProperties">The ingoing properties.</param>
        /// <param name="inStream">The input stream.</param>
        /// <param name="outProperties">The outgoing properties.</param>
        /// <param name="outStream">The output stream.</param>
        /// <remarks>
        /// The handler should not dispose the provided streams.
        /// </remarks>
        public void Handle(ref string mimeType,
            Dictionary<String, String> inProperties,
            Stream inStream,
            Dictionary<String, String> outProperties,
            Stream outStream)
        {
            if(!inProperties.ContainsKey("OperationName"))
                throw Err.Of(this).NotSpecified("OperationName").Ex();
            if(!inProperties.ContainsKey("CID"))
                throw Err.Of(this).NotSpecified("CID").Ex();
            if(!inProperties.ContainsKey("FormatGuid"))
                throw Err.Of(this).NotSpecified("FormatGuid").Ex();

            var cid = inProperties["CID"].ParseAs<Guid>();
            if(cid == Guid.Empty)
                throw Err.Of(this).NotValid("CID").Ex();
            var sid = inProperties.ContainsKey("SID")
                ? inProperties["SID"].ParseAs<Guid>()
                : Guid.Empty;

            var formatGuid = inProperties["FormatGuid"].ParseAs<Guid>();
            if(formatGuid == Guid.Empty)
                throw Err.Of(this).NotValid("FormatGuid").Ex();
            if(!_infrastructure.CheckDependency<IImportExportFormat>(formatGuid))
                throw Err.Of(this).NotFound("Format").Ex();

            string operationName = inProperties["OperationName"];
            if(String.IsNullOrWhiteSpace(operationName))
                throw Err.Of(this).NotValid("FormatGuid").Ex();
            if(!_infrastructure.CheckDependency<IExportOperation>(operationName))
                throw Err.Of<IExportOperation>().NotFound().Ex();

            using(var ioc = _infrastructure.DependencyResolver())
            {
                var operation = ioc.Resolve(operationName)
                    as IExportOperation;
                operation.Properties = inProperties;
                var processor = ioc.Resolve<IOperationProcessor>().Init(new WorkingContext
                {
                    CID = cid,
                    SID = sid,
                    IsSafe = false
                });

                try
                {
                    processor.Execute(operation);
                    processor.Commit();
                }
                catch
                {
                    processor.Rollback();
                    throw;
                }

                var format = ioc.Resolve(formatGuid)
                    as IImportExportFormat;
                format.Write(outStream, operation.Result);
                mimeType = format.MimeType;
            }
        }

        #endregion
    }
}
